﻿using System.IO.Pipelines;
using K9Nano.Updater.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace K9Nano.Updater.Pipes;

public class UpdateContext
{
    private readonly ILogger _logger;

    public UpdateContext(IServiceProvider serviceProvider,
        Action<double>? progressHandler)
    {
        ServiceProvider = serviceProvider;
        ProgressHandler = progressHandler;
        _logger = GetService<ILoggerFactory>().CreateLogger("UpdateContext");
        Options = GetService<IOptions<UpdaterOptions>>().Value;
        TempDirectories = [];
        WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
    }

    public string WorkingDirectory { get; }

    public IServiceProvider ServiceProvider { get; }

    public UpdaterOptions Options { get; }

    public Action<double>? ProgressHandler { get; }


    public Version? CurrentVersion { get; set; }

    public bool Terminated { get; set; }

    public Version? TargetVersion { get; set; }

    public bool Success { get; set; }

    public double Progress { get; private set; }

    /// <summary>
    /// 存放临时文件的路径
    /// </summary>
    public HashSet<string> TempDirectories { get; private set; }

    /// <summary>
    /// 更新包元数据
    /// </summary>
    public ReleaseMetadata? Package { get; internal set; }

    internal IUpdatePipe? CurrentPipe { get; set; }

    internal int CurrentStep { get; set; }

    internal int StepCount { get; set; } = 1;

    /// <summary>
    /// 是否备份成功
    /// </summary>
    public bool Backuped { get; set; }

    /// <summary>
    /// 用于管道报告进度
    /// </summary>
    /// <param name="progress">0-100</param>
    internal void ReportProgress(double progress)
    {
        if (progress <= 0) return;
        if (progress > 100) progress = 100;
        Progress = CurrentStep * 100 / StepCount + progress / StepCount;
        ProgressHandler?.Invoke(Progress);
    }

    internal void ReportProgress(int step, int stepCount)
    {
        if (step <= 0) return;
        if (step > stepCount)
        {
            ReportProgress(100d);
            return;
        }
        ReportProgress(step * 100 / stepCount);
    }

    internal void ReportSuccess()
    {
        Progress = 100d;
        ProgressHandler?.Invoke(Progress);
    }

    internal T GetService<T>() where T : notnull
    {
        return ServiceProvider.GetRequiredService<T>();
    }

    /// <summary>
    /// 在 <see cref="UpdaterOptions.TempPath"/> 获取空目录
    /// </summary>
    /// <param name="directoryName">目录名称</param>
    /// <param name="shouldClean">为true时，创建的目录将被添加到<see cref="TempDirectories"/>，最终被清理</param>
    /// <returns></returns>
    internal string GetEmtpyTempPath(string directoryName, bool shouldClean = true)
    {
        var path = Path.Combine(Options.TempPath, directoryName);
        this.TempDirectories.Add(path);
        return path;
    }

    /// <summary>
    /// 获取下载目录
    /// </summary>
    /// <returns></returns>
    internal string GetDownloadDirectory()
    {
        return GetEmtpyTempPath(TargetVersion!.ToString());
    }

    /// <summary>
    /// 获取备份目录
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UpdaterException"></exception>
    internal string GetBackupDirectory() => CurrentVersion is not null
        ? Path.Combine(WorkingDirectory, $"k9-backup-{CurrentVersion}")
        : throw new UpdaterException("当前版本号无效");

    private void Log(LogLevel logLevel, Exception? ex, string template, params object?[] args)
    {
        if (logLevel >= LogLevel.Error)
        {
            Success = false;
            Terminated = true;
        }

        if (CurrentPipe is null)
        {
            _logger.Log(logLevel, template, args);
            return;
        }

        if (args?.Length > 0)
        {
            _logger.Log(logLevel, exception: ex, message: "【{pipe}】" + template, args: new[] { CurrentPipe.PipeName }.Concat(args).ToArray());
        }
        else
        {
            _logger.Log(logLevel, exception: ex, message: "【{pipe}】" + template, args: [CurrentPipe.PipeName]);
        }
    }

    internal void LogDebug(string template, params object?[] args) => Log(LogLevel.Debug, null, template, args);

    internal void LogInformation(string template, params object?[] args) => Log(LogLevel.Information, null, template, args);

    internal void LogWarning(string template, params object?[] args) => Log(LogLevel.Warning, null, template, args);

    internal void LogError(string template, params object?[] args) => Log(LogLevel.Error, null, template, args);

    internal void LogError(Exception ex, string template, params object?[] args) => Log(LogLevel.Error, ex, template, args);
}
